#include "memory/instruction.h"
#include "cpu/mmu.h"
#include "cpu/register.h"
#include "memory/dram.h"

#include <stdio.h>


//解码的过程
//如果是imm，那么就是一个立即数
//如果是reg，那么就是这个寄存器的value
//如果是mm，那么就是这个内存的物理地址
static uint64_t decode_od(od_t od){
    
    if(od.type==IMM)
    {
        return *((uint64_t *)&od.imm);
    }
    else if(od.type==REG)
    {
        return (uint64_t)od.reg1;             //od.reg1返回的是寄存器的地址，这里要return这个地址对应的值
    }
    else{

        // mm  如果是内存的情况

        // old
        // uint64_t addr = MM_LEN + 0xff;
        // return mm[addr % MM_LEN];

        //先定义好虚拟地址
        uint64_t vaddr =0;

        if (od.type==MM_IMM)
        {
            vaddr = *((uint64_t *)&od.imm);
        }
        else if (od.type==MM_REG)
        {
            vaddr = *(od.reg1);        //od.reg1就是一个指针，*(指针) 就是将这个指针指向的值取出来
        }
        else if (od.type==MM_IMM_REG)
        {
            vaddr = *((uint64_t *)&od.imm) + *(od.reg1);
        }
        else if (od.type==MM_REG1_REG2)
        {
            vaddr = *(od.reg1) + *(od.reg2);
        }
        else if (od.type==MM_IMM_REG1_REG2)
        {
            vaddr = *(od.reg1) + *(od.reg2) + *((uint64_t *)&od.imm);
        }
        else if (od.type==MM_REG2_S)
        {
            vaddr = (*(od.reg2)) * od.scal;
        }
        else if (od.type==MM_IMM_REG2_S)
        {
            vaddr = *((uint64_t *)&od.imm) + (*(od.reg2)) * od.scal;
        }
        else if (od.type==MM_REG1_REG2_S)
        {
            vaddr = *(od.reg1) + (*(od.reg2)) * od.scal;
        }
        else if (od.type==MM_IMM_REG1_REG2_S)
        {
            vaddr = *((uint64_t *)&od.imm) + *(od.reg1) + (*(od.reg2)) * od.scal;
        }

        //直接返回虚拟地址比较好，虚拟地址到物理地址的转换放到具体的指令里面做就好了
        return vaddr;

    }
}


void instruction_cycle(){
    
    //指令是从rip来的
    //以下两种写法都可以
    //inst_t instr = program[reg.rip];       //rip存的是program数组的index
    inst_t *instr = (inst_t *)reg.rip;       //rip存的是真实的物理地址

    //解码的过程
    //如果是imm，那么就是一个立即数
    //如果是reg，那么就是这个寄存器的value
    //如果是mm，那么就是这个内存的物理地址
    uint64_t src = decode_od(instr->src);
    uint64_t dst = decode_od(instr->dst);


    //函数指针数组
    //函数：handler
    //op是euum中计数的，天然可以成为index
    //handler_table是一个数组，数组的每一个项目是一个函数指针，避免了写重复的if else

    //例如：add rax rbx
    //op = add_reg_reg=3
    //handler_table[add_reg_reg] == handler_table[3] == add_reg_reg_handler
    handler_t handler = handler_table[instr->op];
    //add_reg_reg_handler（src=&rax，dst=&rbx）
    handler(src, dst);

    printf("%s\n",instr->code);

}



void init_handler_table()
{
    handler_table[mov_reg_reg] = &mov_reg_reg_handler;
    handler_table[call] = &call_handler;
    handler_table[add_reg_reg] = &add_reg_reg_handler;
    handler_table[push_reg] = &push_reg_handler;
    handler_table[pop_reg] = &pop_reg_handler;
    handler_table[mov_reg_mem]=&mov_reg_mem_handler;
    handler_table[mov_mem_reg]=&mov_mem_reg_handler;
    handler_table[ret]=&ret_handler;



//uint64_t src是寄存器rax的地址，uint64_t dst是寄存器rbx的地址
//*(uint64_t *)src是寄存器rax的值，*(uint64_t *)dst是寄存器rbx的值
void add_reg_reg_handler(uint64_t src, uint64_t dst){

    //add_reg_reg_handler（src=&rax，dst=&rbx）

    /*
        rax pmm[0x1234] = 0x12340000

        rbx pmm[0x1235] = 0xabcd

        src：0x1234

        dst：0x1235

        *(uint64_t *)src = 0x12340000

        *(uint64_t *)dst = 0xabcd

        0x12340000 + 0xabcd = 0x1234abcd

        pmm[0x1235] =  0x1234abcd

    */

    *(uint64_t *)dst = *(uint64_t *)dst + *(uint64_t *)src;
    //更新rip的progamme count
    reg.rip = reg.rip + sizeof(inst_t);
}

void mov_reg_reg_handler(uint64_t src, uint64_t dst){
    
    *(uint64_t *)dst = *(uint64_t *)src;
    //更新rip的progamme count
    reg.rip = reg.rip + sizeof(inst_t);
}

void call_handler(uint64_t src, uint64_t dst)
{
    // src: imm address of called function
    reg.rsp = reg.rsp - 8;
    
    // write return address to rsp memory
    write64bits_dram(va2pa(reg.rsp),reg.rip + sizeof(inst_t));

    //将rip指向add函数的入口指令的首地址
    reg.rip = src;
}



void mov_reg_mem_handler(uint64_t src, uint64_t dst){
    //src:reg  dst:mm vitrual memory
    write64bits_dram(va2pa(dst),*(uint64_t *)src);
    //更新rip的progamme count
    reg.rip = reg.rip + sizeof(inst_t);
}

void mov_mem_reg_handler(uint64_t src, uint64_t dst){
    //src:mm vitrual memory  dst:reg
    *(uint64_t *)dst = read64bits_dram(va2pa(src));
    //更新rip的progamme count
    reg.rip = reg.rip + sizeof(inst_t);
}


void push_reg_handler(uint64_t src, uint64_t dst){
    //src:reg  dst:empty    
    reg.rsp=reg.rsp-0x8; //same as reg.rsp -= 8;
    // write return address to rsp memory
    write64bits_dram(va2pa(reg.rsp),*(uint64_t *)src);
    //更新rip的progamme count
    reg.rip = reg.rip + sizeof(inst_t);
}


void pop_reg_handler(uint64_t src, uint64_t dst)
{
    //src:reg rbp 
    *(uint64_t *)src= read64bits_dram(va2pa(reg.rsp));
    reg.rsp += 8;
    reg.rip=reg.rip+sizeof(inst_t);
}

void ret_handler(uint64_t src, uint64_t dst)
{
    uint64_t ret_addr = read64bits_dram(va2pa(reg.rsp));
    reg.rsp += 8;
    reg.rip=ret_addr;
}
